iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0

首先我們先建立一個名為 SearchBar 的 component,並且在其中加上一個用於顯示資料的視窗 & 視窗的開關。

import React, { useState } from "react";
import cx from "classnames";

const Modal = ({ isOpen, onClose, originArr }) => {
  const [inputValue, setInputValue] = useState("");
  const [filteredData, setFilteredData] = useState([]);

  const changeCase = (inputString, toLowerCase) => {
    if (toLowerCase) {
      return inputString.toLowerCase();
    } else {
      return inputString.toUpperCase();
    }
  };

  const filterFunc = (keyword) => {
    const newKeyword = changeCase(keyword, true);
    const filteredData = originArr.data?.filter((data) => {
      return data.title.toLowerCase().includes(newKeyword)&& keyword !== "";
    });

    return filteredData;
  };

  const handleOnChange = (e) => {
    setInputValue(e.target.value);
    const result = filterFunc(e.target.value);
    setFilteredData(result);
  };

  return (
    <div
      className={cx(
        "gap-[20px] rounded-xl absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-5 z-50 shadow-md border-2 flex flex-wrap justify-center items-center py-10 px-20",
        { block: isOpen, hidden: !isOpen }
      )}
    >
      <div>
        <label className="text-2xl">
          請輸入資料:
          <input
            type="text"
            value={inputValue}
            onChange={(e) => handleOnChange(e)}
            className="border-2"
          />
        </label>
      </div>
      <div className="w-full text-center"></div>
      <ul className="self-start w-full">
        {filteredData?.length !== 0 && (
          <div className="w-full text-center">
            {filteredData &&
              filteredData.map(
                (data, index) =>
                  data && (
                    <li
                      className="my-2 py-2 border-b-2 flex justify-between"
                      data-todo={`todo${index}`}
                      key={index}
                    >
                      <span className="flex-1">{data.title}</span>
                      <div className="flex gap-[20px]"></div>
                    </li>
                  )
              )}
          </div>
        )}
      </ul>
      <button
        onClick={onClose}
        className="bg-red-600 text-white px-4 py-2 rounded-md absolute right-2 top-2"
      >
        X
      </button>
    </div>
  );
};

/**
 * @description a component include toggleBtn & Modal
 * @param {array} data
  */
const SearchBar = (data) => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const handleOpenModal = () => {
    setIsModalOpen(true);
  };

  const handleCloseModal = () => {
    setIsModalOpen(false);
  };

  return (
    <div>
      <button
        onClick={handleOpenModal}
        className="self-center min-w-[80px] text-center rounded mx-2 border-2 px-4 py-1 cursor-pointer shadow-md transition-all bg-white hover:bg-[#e3e8eB] text-gray-500 text-xl font-bold"
      >
        搜尋todo
      </button>

      <Modal isOpen={isModalOpen} onClose={handleCloseModal} originArr={data} />
    </div>
  );
};

export default SearchBar;

我們可以看到這個 component 擁有一個顯示資料的視窗 & 視窗的開關,並且 Modal 接收的參數分別為是否顯示&原始資料,而SearchBar則接收來自於父層的完整資料。

接下來我們整理一下資料流,看看是否和我們預期的一致。
首先SearchBar負責接收父層傳入的 array,接下來資料會進入到Modal,當 user 在輸入欄輸入資料的時候就會觸發onChange,這時候我們就可以得到 keyWord 的資料,並且 keyword 會透過changeCase這個 Function 轉變為小寫,而剛剛傳入的資料則會透過 filter 這個 Method 找出 title 包含 keyWord 的資料,並回傳給我們,最後我們將篩選後的資料塞進filteredData,最後在 return 的部分,使用 map 將我們獲得的資料 render 到畫面上。

這邊需要注意的地方就在於這個部分

const filteredData = originArr.data?.filter((data) => {
    return data.title.toLowerCase().includes(newKeyword) && keyword !== "";
});

至於為什麼呢,因為當user將輸入欄位的資料清空,這時候條件會是string.includes(''),並且這會是成立的,所以我們會發現所有的資料都被return進入到filteredData中,所以這邊我們需要加上一個條件,keyword不能是空的。

這樣才能保證我們的的資料不會全部被render出來。


上一篇
資料查詢
下一篇
來測試一下我們的元件吧(TDD & Jest)
系列文
30天製作與眾不同的TodoList吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言